hover: true });
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
reactive: params.reactive,
- track_hover: params.reactive });
+ track_hover: params.reactive,
+ can_focus: params.reactive });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.active = false;
if (params.reactive && params.activate) {
- this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
- this.emit('activate', event);
- }));
+ this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent));
+ this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
}
if (params.reactive && params.hover)
- this.actor.connect('notify::hover', Lang.bind(this, this._hoverChanged));
+ this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
+ if (params.reactive)
+ this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
},
_onStyleChanged: function (actor) {
this._spacing = actor.get_theme_node().get_length('spacing');
},
- _hoverChanged: function (actor) {
+ _onButtonReleaseEvent: function (actor, event) {
+ this.emit('activate', event);
+ return true;
+ },
+
+ _onKeyPressEvent: function (actor, event) {
+ let symbol = event.get_key_symbol();
+
+ if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
+ this.emit('activate', event);
+ return true;
+ }
+ return false;
+ },
+
+ _onKeyFocusIn: function (actor) {
+ this.setActive(true);
+ },
+
+ _onHoverChanged: function (actor) {
this.setActive(actor.hover);
},
if (activeChanged) {
this.active = active;
- if (active)
+ if (active) {
this.actor.add_style_pseudo_class('active');
- else
+ this.actor.grab_key_focus();
+ } else
this.actor.remove_style_pseudo_class('active');
this.emit('active-changed', active);
}
this.emit('destroy');
},
- handleKeyPress: function(event) {
- return false;
- },
-
// true if non descendant content includes @actor
contains: function(actor) {
return false;
_init: function(value) {
PopupBaseMenuItem.prototype._init.call(this, { activate: false });
+ this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
+
if (isNaN(value))
// Avoid spreading NaNs around
throw TypeError('The slider value must be a number');
return this._value;
},
- handleKeyPress: function(event) {
+ _onKeyPressEvent: function (actor, event) {
let key = event.get_key_symbol();
- if (key == Clutter.Right || key == Clutter.Left) {
- let delta = key == Clutter.Right ? 0.1 : -0.1;
+ if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
+ let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
this._value = Math.max(0, Math.min(this._value + delta, 1));
this._slider.queue_repaint();
this.emit('value-changed', this._value);
this._boxWrapper.add_actor(this._box);
this.actor.add_style_class_name('popup-menu');
+ global.focus_manager.add_group(this.actor);
+
+ if (sourceActor._delegate instanceof PopupSubMenuMenuItem) {
+ this._isSubMenu = true;
+ this.actor.reactive = true;
+ this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
+ }
+
this.isOpen = false;
this._activeMenuItem = null;
},
}
},
- open: function(submenu) {
+ open: function() {
if (this.isOpen)
return;
- this.emit('opening');
-
let primary = global.get_primary_monitor();
// We need to show it now to force an allocation,
let menuWidth = natWidth, menuHeight = natHeight;
// Position the non-pointing axis
- if (submenu) {
+ if (this._isSubmenu) {
if (this._arrowSide == St.Side.TOP || this._arrowSide == St.Side.BOTTOM) {
// vertical submenu
if (sourceY + sourceHeigth + menuHeight + this._gap < primary.y + primary.height)
if (!this.isOpen)
return;
- this.emit('closing');
-
if (this._activeMenuItem)
this._activeMenuItem.setActive(false);
this.actor.reactive = false;
this.emit('open-state-changed', false);
},
-
toggle: function() {
if (this.isOpen)
this.close();
this.open();
},
- handleKeyPress: function(event, submenu) {
- if (!this.isOpen || (submenu && !this._activeMenuItem))
- return false;
- if (this._activeMenuItem && this._activeMenuItem.handleKeyPress(event))
- return true;
- switch (event.get_key_symbol()) {
- case Clutter.space:
- case Clutter.Return:
- if (this._activeMenuItem)
- this._activeMenuItem.activate(event);
+ _onKeyPressEvent: function(actor, event) {
+ // Move focus back to parent menu if the user types Left.
+ // (This handler is only connected if the PopupMenu is a
+ // submenu.)
+ if (this.isOpen &&
+ this._activeMenuItem &&
+ event.get_key_symbol() == Clutter.KEY_Left) {
+ this._activeMenuItem.setActive(false);
return true;
- case Clutter.Down:
- case Clutter.Up:
- let items = this._box.get_children().filter(function (child) { return child.visible && child.reactive; });
- let current = this._activeMenuItem ? this._activeMenuItem.actor : null;
- let direction = event.get_key_symbol() == Clutter.Down ? 1 : -1;
-
- let next = findNextInCycle(items, current, direction);
- if (next) {
- next._delegate.setActive(true);
- return true;
- }
- break;
- case Clutter.Left:
- if (submenu) {
- this._activeMenuItem.setActive(false);
- return true;
- }
- break;
}
return false;
_init: function(text) {
PopupBaseMenuItem.prototype._init.call(this, { activate: false, hover: false });
this.actor.connect('enter-event', Lang.bind(this, this._mouseEnter));
+ this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this.label = new St.Label({ text: text });
this.addActor(this.label);
setActive: function(active) {
if (this.menu) {
if (active)
- this.menu.open(true);
+ this.menu.open();
else
this.menu.close();
}
PopupBaseMenuItem.prototype.setActive.call(this, active);
},
- handleKeyPress: function(event) {
+ _onKeyPressEvent: function(actor, event) {
if (!this.menu)
return false;
- if (event.get_key_symbol() == Clutter.Right) {
+ if (event.get_key_symbol() == Clutter.KEY_Right) {
this.menu.activateFirst();
return true;
}
- return this.menu.handleKeyPress(event, true);
+ return false;
},
contains: function(actor) {
this.grabbed = false;
this._eventCaptureId = 0;
+ this._keyPressEventId = 0;
this._enterEventId = 0;
this._leaveEventId = 0;
this._activeMenu = null;
this._delayedMenus = [];
},
- addMenu: function(menu, noGrab, position) {
+ addMenu: function(menu, position) {
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
activateId: menu.connect('activate', Lang.bind(this, this._onMenuActivated)),
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
enterId: 0,
- buttonPressId: 0
+ focusId: 0
};
let source = menu.sourceActor;
if (source) {
- menudata.enterId = source.connect('enter-event', Lang.bind(this, this._onMenuSourceEnter, menu));
- if (!noGrab)
- menudata.buttonPressId = source.connect('button-press-event', Lang.bind(this, this._onMenuSourcePress, menu));
+ menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
+ menudata.focusId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
}
if (position == undefined)
if (menudata.enterId)
menu.sourceActor.disconnect(menudata.enterId);
- if (menudata.buttonPressId)
- menu.sourceActor.disconnect(menudata.buttonPressId);
+ if (menudata.focusId)
+ menu.sourceActor.disconnect(menudata.focusId);
this._menus.splice(position, 1);
},
Main.pushModal(this._owner.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
+ this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
// captured-event doesn't see enter/leave events
this._enterEventId = global.stage.connect('enter-event', Lang.bind(this, this._onEventCapture));
this._leaveEventId = global.stage.connect('leave-event', Lang.bind(this, this._onEventCapture));
ungrab: function() {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
+ global.stage.disconnect(this._keyPressEventId);
+ this._keyPressEventId = 0;
global.stage.disconnect(this._enterEventId);
this._enterEventId = 0;
global.stage.disconnect(this._leaveEventId);
this._leaveEventId = 0;
- Main.popModal(this._owner.actor);
-
this.grabbed = false;
+ Main.popModal(this._owner.actor);
},
_onMenuOpenState: function(menu, open) {
- if (!open && menu == this._activeMenu)
- this._activeMenu = null;
- else if (open)
+ if (open) {
this._activeMenu = menu;
+ if (!this.grabbed)
+ this.grab();
+ } else if (menu == this._activeMenu) {
+ this._activeMenu = null;
+ if (this.grabbed)
+ this.ungrab();
+ }
},
- _onMenuSourceEnter: function(actor, event, menu) {
+ _onMenuSourceEnter: function(menu) {
if (!this.grabbed || menu == this._activeMenu)
return false;
return false;
},
- _onMenuSourcePress: function(actor, event, menu) {
- if (this.grabbed)
- return false;
- this.grab();
- return false;
- },
-
_onMenuActivated: function(menu, item) {
if (this.grabbed)
this.ungrab();
|| (eventType == Clutter.EventType.KEY_PRESS && event.get_key_symbol() == Clutter.Escape)) {
this._closeMenu();
return true;
- } else if (eventType == Clutter.EventType.KEY_PRESS
- && this._activeMenu != null
- && this._activeMenu.handleKeyPress(event, false)) {
- return true;
- } else if (eventType == Clutter.EventType.KEY_PRESS
- && this._activeMenu != null
- && (event.get_key_symbol() == Clutter.Left
- || event.get_key_symbol() == Clutter.Right)) {
- let direction = event.get_key_symbol() == Clutter.Right ? 1 : -1;
+ } else if (activeMenuContains || this._eventIsOnAnyMenuSource(event)) {
+ return false;
+ }
+
+ return true;
+ },
+
+ _onKeyPressEvent: function(actor, event) {
+ if (!this.grabbed || !this._activeMenu)
+ return false;
+ if (!this._eventIsOnActiveMenu(event))
+ return false;
+
+ let symbol = event.get_key_symbol();
+ if (symbol == Clutter.Left || symbol == Clutter.Right) {
+ let direction = symbol == Clutter.Right ? 1 : -1;
let pos = this._findMenu(this._activeMenu);
let next = this._menus[mod(pos + direction, this._menus.length)].menu;
if (next != this._activeMenu) {
- this._activeMenu.close();
- next.open(false);
+ let oldMenu = this._activeMenu;
+ this._activeMenu = next;
+ oldMenu.close();
+ next.open();
next.activateFirst();
}
return true;
- } else if (activeMenuContains || this._eventIsOnAnyMenuSource(event)) {
- return false;
}
- return true;
+ return false;
},
_closeMenu: function() {
if (this._activeMenu != null)
this._activeMenu.close();
- this.ungrab();
}
};